/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package WinOnJET;

import java.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.microedition.io.Connector;
import javax.microedition.io.file.*;
import sUtils.*;
import java.util.*;
import javax.bluetooth.*;



public class WinOnJET extends MIDlet implements CommandListener, EventListener {
    View view;
    Socket socket, socket2;
    MotionSensor motion;
    VolumeKeys volume;
    Command menu, spare;
    Timer udpTimer;
    boolean opened=false;
    
    public void startApp() {
        Lang.init();
        spare = new Command (" ", Command.ITEM, 0);
        menu = new Command (Lang.word("Menu","Menü"), Command.ITEM, 1);

        view = new View(this);
        socket = new Socket(this,9999);
        socket2 = new Socket(this,9997);
        motion = new MotionSensor(this);
        volume = new VolumeKeys(this);
        view.addCommand(spare);
        view.addCommand(menu);
        view.setCommandListener(this);
        Display.getDisplay(this).setCurrent(view);


/*
        LocalDevice localDevice = null;
        DiscoveryAgent discoveryAgent = null;
        int bt=0;
        try {
            localDevice = LocalDevice.getLocalDevice(); bt=1;
            localDevice.setDiscoverable(DiscoveryAgent.NOT_DISCOVERABLE); bt=2;
            discoveryAgent = localDevice.getDiscoveryAgent(); bt=3;
            RemoteDevice dev[] = discoveryAgent.retrieveDevices(DiscoveryAgent.PREKNOWN); bt=4;
            for ( int i=0; i<dev.length; i++) {
                view.info("bt "+i+" "+dev[i].getBluetoothAddress());
            }
        } catch(Exception e) { view.info("BT exception at "+ bt); }
       */
        socket.open();
        socket2.open();
        socket.broadcastServerInfo();
      //  udpTimer = new Timer();
      //  udpTimer.schedule( new infoTask(socket), 2000, 3000 );
    }

    public void pauseApp() {}

    public void destroyApp(boolean unconditional) {
      //  udpTimer.cancel();
        socket.close();
        socket2.close();
        volume.stop();
    }

    public void commandAction( Command cmd, Displayable d )
    {
        if (cmd==menu) { view.showMenu(); }
    }

    public void send( String s ) { socket.send(s); }
    public void info( String s, boolean add ) { view.info(s,add); }
    public void setJPG( byte [] jpg, boolean c ) { view.setJPG(jpg,c); }
    public void setMouse( int x, int y ) { view.setMouse(x,y); }
    public void setOptionVal( String id, int val ) { view.menu.setState(id, val); }

    public void onOpen( Socket s )
    {
        if (socket==s)
        {
            view.info(Lang.word("connected","verbunden"), true);
            socket.send(">canvas "+view.getWidth()+" "+view.getHeight()+" 270\n");
        }
    }
    public void onClose( Socket s )
    {
        if (socket==s)
        {
            view.setJPG(null,false);
            view.info(Lang.word("disconnected","Verbindung beendet"), true);
        }
    }
    public void onPointer( String cmd, int x, int y ) { socket.send(cmd+" "+x+" "+y+"\n"); }
    public void onEvent( String id, String n )
    {
        if (id.equalsIgnoreCase("volume")) socket.send(">volume "+n+"\n");
        if (id.equalsIgnoreCase("motion")) socket.send(">motion "+n+"\n");
        if (id.equalsIgnoreCase("vkey")) socket.send(">vkey "+n+"\n");
    }
    public void onMenu( String id )
    {
        if (id!=null)
        {
            if (id.equalsIgnoreCase("back")) { Display.getDisplay(this).setCurrent(view); }
            if (id.equalsIgnoreCase("quit")) { destroyApp(false); notifyDestroyed(); }
            if (id.equalsIgnoreCase("conn")) { socket.open(); socket2.open(); socket.broadcastServerInfo(); }
            if (id.equalsIgnoreCase("webcam")) { socket.opt("webcam"); }
            if (id.equalsIgnoreCase("video")) { socket.opt("video"); }
            if (id.equalsIgnoreCase("zoom")) { socket.opt("zoom"); }
            if (id.equalsIgnoreCase("trayicon")) { socket.opt("trayicon"); }
            if (id.equalsIgnoreCase("vkeys")) { view.togglePad(); }
        }
    }
    public void onRequest( String id, String n)
    {
        if (id.equalsIgnoreCase("photos"))
        {
            Files f = new Files("/");
            int ret = f.collectFiles("Photo/", true);
            ret += f.collectFiles("MMC/Images/", true);
            String s = ">photos ";
            for ( int i=0; i<f.fileCount(); i++ ) s = s + f.file(i) + "|";
            s = s + "\n";
            socket.send(s);
        }

        if (id.equalsIgnoreCase("photo"))
        {
            try {
                FileConnection f = (FileConnection) Connector.open(n,Connector.READ);
                int len = (int)f.fileSize(), pos=0, slen;
                byte temp [] = new byte[1024];
                socket2.send(">photo "+len+" "+n+"\n");
                InputStream is = f.openInputStream();
                while (pos<len)
                {
                    slen = len-pos; if (slen>1024) slen=1024;
                    slen = is.read(temp, 0, slen);
                    socket2.sendBytes(temp,slen);
                    pos+=slen;
                }
                is.close();
                f.close();
            } catch(Exception e ) {}
        }

        if (id.equalsIgnoreCase("cam"))
        {
            volume.mute(true);
            byte raw[] = Images.cameraSnapshotRaw(960, 1600, false);
            volume.mute(false);
            if (raw!=null) try {
                int len = (int)raw.length;
                socket2.send(">cam "+len+" 960 1600\n");
                socket2.sendBytes(raw,len);
            } catch(Exception e ) {}
        }
    }

    class infoTask extends TimerTask {
        private Socket socket;
        infoTask( Socket s ){ socket = s;  }
        public void run() {  socket.broadcastServerInfo(); }
    }
//-----------------------------------------------------------------------
// THE VIEW:
    public class View extends Canvas
    {
        public Image bg = null;
        public Menu menu = new Menu();
        WinOnJET parent;
        String info[] = new String[32], vkey = null;
        boolean cursor = false, pad=false;
        int mousex=-1, mousey=-1, caps=0;
        Keypad keypad= new Keypad();
        
        public View( WinOnJET m )
        {
            parent = m;
            menu.init( getWidth(), getHeight(), 0, true );
            menu.add( "Beenden", "quit" );
            menu.add( null, null );
            menu.add( "Connect", "conn" );
            menu.add( null, null );
            menu.add( "Trayicon", "trayicon" );
            menu.add( "Spycam", "webcam" );
            menu.add( "Video", "video" );
            menu.add( "Zoom", "zoom" );
            menu.add( "Keyboard", "vkeys" );

            menu.setState("trayicon", 1);
            menu.setState("video", 1);
            menu.setState("webcam", 0);
            menu.setState("zoom", 0);
        }

        public void showMenu()
        {
            menu.show();
            repaint();
        }

        public void info( String s, boolean add )
        {
            if (add) for ( int i=info.length-1; i>0; i-- ) info[i]=info[i-1];
            info[0]=s;
            repaint();
        }

        public void togglePad() { pad=!pad; menu.setState("vkeys", pad?1:0); repaint(); }

        public void paint( Graphics g )
        {
            int w = getWidth();
            int h = getHeight();
            g.setColor(0x40);
            g.fillRect(0,0,w,h);

            if (bg!=null) g.drawImage(bg, 0, 0, 0);

            if (mousex>0 && cursor)
            {
                g.setColor(0xc0);
                g.fillRect(mousex, 0, 1, h);
                g.fillRect(0, mousey, w, 1);
            }
            if (bg==null)
            {
                // draw debug info:
                Font f = Font.getFont(Font.FACE_SYSTEM,Font.STYLE_PLAIN,Font.SIZE_SMALL);
                int fh = f.getHeight();
                g.setColor(0xffffff);
                g.setFont( f);
                for ( int i=0; i<info.length; i++ ) if (info[i]!=null) g.drawString(info[i], 2, 2+i*fh, 0);
            }
            if(pad) { g.setClip(0,0,getWidth(),getHeight()); keypad.paint(g, caps); }
            menu.paint(g);
        }

        public void setJPG( byte [] jpg, boolean c )
        {
            bg = jpg==null ? null : Image.createImage(jpg, 0, jpg.length);
            cursor = c;
            repaint();
        }
        public void setMouse( int x, int y )
        {
            mousex=(x*getWidth())/1000; mousey=(y*getHeight())/1000; repaint();
        }

        public void onKey( String s )
        {
            if (s!=null)
            {
                if (parent!=null) Display.getDisplay(parent).vibrate(5);
                if (s.equals("Space")) parent.send(">vkey 0x20\n"); else
                if (s.equals("Cap")) { caps = caps==0 ? 1 : 0; repaint(); } else
                if (s.equals("<")) parent.send(">vkey 0x25\n"); else
                if (s.equals(">")) parent.send(">vkey 0x27\n"); else
                if (s.equals("Del")) parent.send(">vkey 0x2E\n"); else
                parent.send(">char "+(int)(s.charAt(caps))+"\n");
            }
        }
        public void onPointer( String cmd, int x, int y ) {
            if (vkey==null) parent.onPointer( cmd, (x*1000)/getWidth(), (y*1000)/getHeight() );
        }
        public void pointerPressed( int x, int y ) {
            if (menu.active) parent.onMenu(menu.hit(x, y)); else
            {
                vkey = pad ? keypad.hitTest(x, y) : null;
                if (vkey!=null) onKey(vkey);
                onPointer(">touchdown",x,y);
            }
        }
        public void pointerDragged( int x, int y ) { 
            if(!menu.active) onPointer(">touchdrag",x,y);
        }
        public void pointerReleased( int x, int y ) {
            if (menu.active) { menu.release(); repaint(); } else
                onPointer(">touchrelease",x,y); vkey=null;
        }
        public void keyPressed( int key ) { send(">key "+key+"\n"); }

        public void screenshot( String name )
        {
            Image img = Images.graphicsImage( this );
            paint( img.getGraphics() );
            Images.save( img, name);
        }
    }

//-----------------------------------------------------------------------
// THE SOCKET CONNECTION:
    public class Socket implements Runnable
    {
        WinOnJET parent;
        Thread thread = null;
        boolean running=false;
        ServerSocketConnection server = null;
        SocketConnection client = null;
        DataInputStream dis = null;
        DataOutputStream dos = null;
        public int port = 9999;

        public Socket( WinOnJET p, int pt ) { parent = p; port=pt; }

        public void open()
        {
            close();
            try {
                server = (ServerSocketConnection) Connector.open("socket://:"+port);
                parent.info("Server "+server.getLocalAddress()+":"+port, true);
                //host = server.getLocalAddress();
                thread = new Thread(this);
                thread.start();
            } catch( Exception e ) { }
        }

        public void close()
        {
            if (running)
            {
                running = false;
                try { if (thread!=null) thread.wait(1000); } catch (Exception e){}
            }
            try {
                if (dis!=null) dis.close();
                if (dos!=null) dos.close();
                if (client!=null) client.close();
                if (server!=null) server.close();
            } catch (Exception e){}
            dis=null; dos=null; client=null; server=null; thread=null;
        }

        public boolean opt( String o )
        {
            return send(">opt "+o+"\n");
        }

        public boolean send( String command )
        {
            boolean rc = false;
            if (dos!=null && command!=null)
            {
                try {
                    byte b []  = new byte[command.length()];
                    for ( int i=0; i<command.length(); i++ ) b[i]=(byte)(command.charAt(i)&0xff);
                    dos.write(b, 0, b.length);
                    rc = true;
                } catch( Exception e ) { rc=false; }
            }
            return rc;
        }

        
        public boolean sendBytes(byte [] b, int len)
        {
            boolean rc = false;
            if (dos!=null)
            {
                try{ dos.write(b,0,len); rc=true; } catch( Exception e ) { rc=false; }
            }
            return rc;
        }

        public void broadcastServerInfo()
        {
            System.gc();

            UDPDatagramConnection udp=null;
            try {
                udp = (UDPDatagramConnection) Connector.open("datagram://localhost:9998");
            } catch(Exception e)
            { udp=null;
              parent.info(Lang.word("cannot send server info","Kann Daten nicht senden"),true);
              parent.info(Lang.word("Try reboot your mobile!","Probieren Sie einen Handyneustart."),true);
            }

            try { Thread.sleep(500); } catch(Exception e){}

            if (udp!=null)
            {
                String s = "";
                try { s = udp.getLocalAddress(); } catch(Exception e) { s=""; }
                parent.info("IP "+s,true);
                byte data [] = new byte[64];
                for ( int i=0; i<64; i++ )
                    data[i]=( s!=null ) ? ( i<s.length()  ? (byte)s.charAt(i) : 0) : 0;

                int pos = s.lastIndexOf('.');
                for (int i=1; i<=255; i++)
                {
                    String ip=s.substring(0, pos+1)+i;
                    if (!ip.equalsIgnoreCase(s))
                    {
                        String t = "datagram://"+ip+":9998";
                        parent.info("UDP to "+ip,false);
                        try { udp.send( udp.newDatagram(data, 64, t) ); } catch(Exception e) {  }
                    }
                }

            }
        }

        public boolean onCommand( String c )
        {
            boolean rc=true;
            String arg[] = Strings.split(c,' ');
            
            if (arg[0].equals("quit"))
            {
                return false;
            }
            else
            if (arg[0].equals("cursor")||arg[0].equals("mouse"))
            {
                int x = 0, y = 0;
                try { x=Integer.parseInt(arg[1]); } catch (Exception e) {x=0;}
                try { y=Integer.parseInt(arg[2]); } catch (Exception e) {y=0;}
                parent.setMouse(x,y);

            }
            else
            if (arg[0].equals("jpg") || arg[0].equals("cam"))
            {
                int len = 0, rlen;
                try { len=Integer.parseInt(arg[1]); } catch (Exception e) {}

                byte [] jpg = new byte [len];
                for ( int i=0; i<len;)
                {
                    rlen = len-i;
                    try {
                        i += dis.read(jpg, i, rlen>=2048 ? 2048 : rlen );
                    } 
                    catch( IOException e )
                    {
                       // rc=false;
                    }
                }
                if (rc) parent.setJPG(jpg, arg[0].equals("jpg")); else parent.setJPG(null, false);
            }
            else
            if (arg[0].equals("optval"))
            {
                String id = arg.length>1 ? arg[1] : ""; int val = 0;
                try { val=Integer.parseInt(arg[2]); } catch (Exception e) {val=0;}
                parent.setOptionVal(id,val);

            }
            else
            if (arg[0].equals("request")||arg[0].equals("req"))
            {
                parent.onRequest( arg[1], arg.length>2 ? arg[2] : null );
            }
            return rc;
        }

        public void run()
        {
          //  if (port==9997) Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
            try {
                client = (SocketConnection) server.acceptAndOpen();
                if (port==9999) parent.info(Lang.word("client connected","WinOnJET mit PC verbunden"),true);
                client.setSocketOption(SocketConnection.DELAY, 0);
                client.setSocketOption(SocketConnection.KEEPALIVE, 0);
                dis = client.openDataInputStream();
                dos = client.openDataOutputStream();
            } catch(Exception e) 
            { if (port==9999) parent.info(Lang.word("Exception socket open","Fehler beim Verbinden"),true); }

            parent.onOpen(this);
            
            int c=0;
            running = true;
            StringBuffer sb;
            boolean succ = true;
            while(running)
            {
                sb = new StringBuffer();
                try {
                    while ( (c=dis.read())!='>' ) {} 
                    while ( (c=dis.read())!='\n' ) sb.append((char)c);
                    succ =  onCommand(sb.toString());
                } catch(Exception e)
                {
                    if (port==9999) parent.info(Lang.word("Exception socket read","Fehler beim Datenlesen"),true);
                    succ=false;
                }
                if (!succ) try { Thread.sleep(3000); } catch(Exception e) {}
            }
            running=false;
            onClose(this);
            notifyAll();
        }
    }
}

